﻿using System;
using System.Collections.Generic;
using System.Linq;
using GeoAPI.Geometries;
using NetTopologySuite.Utilities;

namespace NetTopologySuite.Geometries.Utilities
{
	public class GeometryEditor
	{
		public GeometryEditor()
		{
		}

		public GeometryEditor(IGeometryFactory factory)
		{
			this._factory = factory;
		}

		public bool CopyUserData
		{
			get
			{
				return this._isUserDataCopied;
			}
			set
			{
				this._isUserDataCopied = value;
			}
		}

		public IGeometry Edit(IGeometry geometry, GeometryEditor.IGeometryEditorOperation operation)
		{
			if (this._factory == null)
			{
				this._factory = geometry.Factory;
			}
			IGeometry geometry2 = this.EditInternal(geometry, operation);
			if (this._isUserDataCopied)
			{
				geometry2.UserData = geometry.UserData;
			}
			return geometry2;
		}

		private IGeometry EditInternal(IGeometry geometry, GeometryEditor.IGeometryEditorOperation operation)
		{
			if (geometry is IGeometryCollection)
			{
				return this.EditGeometryCollection((IGeometryCollection)geometry, operation);
			}
			if (geometry is IPolygon)
			{
				return this.EditPolygon((IPolygon)geometry, operation);
			}
			if (geometry is IPoint)
			{
				return operation.Edit(geometry, this._factory);
			}
			if (geometry is ILineString)
			{
				return operation.Edit(geometry, this._factory);
			}
			Assert.ShouldNeverReachHere("Unsupported Geometry classes should be caught in the GeometryEditorOperation.");
			return null;
		}

		private IPolygon EditPolygon(IPolygon polygon, GeometryEditor.IGeometryEditorOperation operation)
		{
			IPolygon polygon2 = (IPolygon)operation.Edit(polygon, this._factory);
			if (polygon2 == null)
			{
                polygon2 = this._factory.CreatePolygon((ICoordinateSequence)null);
			}
			if (polygon2.IsEmpty)
			{
				return polygon2;
			}
			ILinearRing linearRing = (ILinearRing)this.Edit(polygon2.ExteriorRing, operation);
			if (linearRing == null || linearRing.IsEmpty)
			{
				return this._factory.CreatePolygon(null, null);
			}
			List<ILinearRing> list = new List<ILinearRing>();
			for (int i = 0; i < polygon2.NumInteriorRings; i++)
			{
				ILinearRing linearRing2 = (ILinearRing)this.Edit(polygon2.GetInteriorRingN(i), operation);
				if (linearRing2 != null && !linearRing2.IsEmpty)
				{
					list.Add(linearRing2);
				}
			}
			return this._factory.CreatePolygon(linearRing, list.ToArray());
		}

		private IGeometryCollection EditGeometryCollection(IGeometryCollection collection, GeometryEditor.IGeometryEditorOperation operation)
		{
			IGeometryCollection geometryCollection = (IGeometryCollection)operation.Edit(collection, this._factory);
			IList<IGeometry> list = new List<IGeometry>();
			for (int i = 0; i < geometryCollection.NumGeometries; i++)
			{
				IGeometry geometry = this.Edit(geometryCollection.GetGeometryN(i), operation);
				if (geometry != null && !geometry.IsEmpty)
				{
					list.Add(geometry);
				}
			}
			if (geometryCollection is IMultiPoint)
			{
				return this._factory.CreateMultiPoint(list.Cast<IPoint>().ToArray<IPoint>());
			}
			if (geometryCollection is IMultiLineString)
			{
				return this._factory.CreateMultiLineString(list.Cast<ILineString>().ToArray<ILineString>());
			}
			if (geometryCollection is IMultiPolygon)
			{
				return this._factory.CreateMultiPolygon(list.Cast<IPolygon>().ToArray<IPolygon>());
			}
			return this._factory.CreateGeometryCollection(list.ToArray<IGeometry>());
		}

		private IGeometryFactory _factory;

		private bool _isUserDataCopied;

		public interface IGeometryEditorOperation
		{
			IGeometry Edit(IGeometry geometry, IGeometryFactory factory);
		}

		public class NoOpGeometryOperation : GeometryEditor.IGeometryEditorOperation
		{
			internal NoOpGeometryOperation()
			{
			}

			public IGeometry Edit(IGeometry geometry, IGeometryFactory factory)
			{
				return geometry;
			}
		}

		public abstract class CoordinateOperation : GeometryEditor.IGeometryEditorOperation
		{
			public IGeometry Edit(IGeometry geometry, IGeometryFactory factory)
			{
				if (geometry is ILinearRing)
				{
					return factory.CreateLinearRing(this.Edit(geometry.Coordinates, geometry));
				}
				if (geometry is ILineString)
				{
					return factory.CreateLineString(this.Edit(geometry.Coordinates, geometry));
				}
				if (geometry is Point)
				{
					Coordinate[] array = this.Edit(geometry.Coordinates, geometry);
					return factory.CreatePoint((array.Length != 0) ? array[0] : null);
				}
				return geometry;
			}

			public abstract Coordinate[] Edit(Coordinate[] coordinates, IGeometry geometry);
		}

		public class CoordinateSequenceOperation : GeometryEditor.IGeometryEditorOperation
		{
			public CoordinateSequenceOperation() : this((ICoordinateSequence s, IGeometry g) => s)
			{
			}

			public CoordinateSequenceOperation(Func<ICoordinateSequence, IGeometry, ICoordinateSequence> editSequence)
			{
				this.EditSequence = editSequence;
			}

			public IGeometry Edit(IGeometry geometry, IGeometryFactory factory)
			{
				ILinearRing linearRing = geometry as ILinearRing;
				if (linearRing != null)
				{
					return factory.CreateLinearRing(this.EditSequence(linearRing.CoordinateSequence, geometry));
				}
				ILineString lineString = geometry as ILineString;
				if (lineString != null)
				{
					return factory.CreateLineString(this.EditSequence(lineString.CoordinateSequence, geometry));
				}
				IPoint point = geometry as IPoint;
				if (point != null)
				{
					return factory.CreatePoint(this.EditSequence(point.CoordinateSequence, geometry));
				}
				return geometry;
			}

			protected Func<ICoordinateSequence, IGeometry, ICoordinateSequence> EditSequence { get; set; }
		}
	}
}
